package model.constraints.inherited;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Vector;
import java.util.Hashtable;

import model.constraints.AbstractConstraint;
import model.constraints.synthesized.SyntAttributesXDF;

import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;

public class ConfigurationSet extends LinkedHashSet<Configuration>  implements Cloneable {
	
	private static final long serialVersionUID = 1L;	
	private String ref;
	private AbstractConstraint kconf;

	public ConfigurationSet(){}
	
	ConfigurationSet(ArrayList<Configuration> a) {
		super.addAll(a);
	}
		
	public static ConfigurationSet empty() {
		return new ConfigurationSet();
	}

	public boolean add(Configuration c) {
		return super.add(c);
	}

	public static ConfigurationSet singleton(Configuration c) {
		ConfigurationSet cs = new ConfigurationSet();
		cs.add(c);
		return cs;
	}

	/**
	 * Checks if there's automaton-changing
	 * @param l label 
	 * @return
	 */
	public ConfigurationSet changeConfiguration(Label l) {
		ConfigurationSet cs = new ConfigurationSet();
		
		for (Iterator<Configuration> i = this.iterator(); i.hasNext();) {
			Configuration c = (Configuration) i.next();
			Fsa f = c.getFsa(); 
			State sf = c.getState();
			AbstractConstraint k = (AbstractConstraint) c.getKey();
						
			addConfiguration(sf, cs, l, f, k);//make transitions

			//verify final state
			if (f.get_final_states().contains(sf)) {
				int fposition = k.getFsaPosition(f);
				
				//verify context position
				if (fposition == 1) {				
					//walks in left side automaton
					f = k.getSourceFsa();
					sf = f.get_start_state();
					addConfiguration(sf, cs, l, f, k);

					//walks in right side automaton
					f = k.getTargetFsa();
					sf = f.get_start_state();
					addConfiguration(sf, cs, l, f, k);
				}
			}
		}		
		
		//  HERE WE HAVE TO ORDER THE ENTIRE CONFIGURATION - IN CS
		// FIRST: CONTEXT automata, automata LEFT, RIGHT automata
		//                   SORT BY STATE		
		ArrayList<Configuration> a = new ArrayList<Configuration>(cs);
		Collections.sort(a);			
		
		ConfigurationSet csNew = new ConfigurationSet(a);		
		
		return csNew;	
	}
	
	/**
	 * This functions creates a new configuration and inserts it set configuration
	 * @param State sf
	 * @param ConfigurationSet
	 * @param Label label
	 * @param Fsa	fsa
	 * @param Abstract Constraint 
	 */
	public void addConfiguration(State stateFsa, ConfigurationSet cs, Label label, Fsa fsa, AbstractConstraint constraint) {		
		Alphabet alphabet = fsa.get_alphabet();
		Label star = new Label("*");
		State s;
		StateSet ss;
		Iterator<State> i;
		
		if (alphabet.contains(label)) {
			
			ss = fsa.get_dest_states(stateFsa, label);
			
			for (i = ss.iterator(); i.hasNext();) {
				s = (State) i.next();
				cs.add(new Configuration(s, fsa, constraint));
			}
		}
		
		if (alphabet.contains(star)) {								
			if (fsa.get_dest_states(stateFsa,star) != null) {
			   ss = fsa.get_dest_states(stateFsa,star);  
			   
			   for (i = ss.iterator(); i.hasNext();) {
				   s = (State) i.next();
				   Configuration c = new Configuration(s, fsa, constraint);
				   
				   if (!cs.contains(c))
					   cs.add(c);
			   }
		    }
		}
	}

	public Vector<SyntAttributesXDF> keyValidationData(Vector<SyntAttributesXDF> syntVec, int k, String data, String posAnt, 
			                        				String posAt, String nodeName,
			                        				Hashtable<String,ArrayList<Inters>> htIntersXfd) 
	{
		// Validation for leaf nodes, considering their ancestor node.
		Configuration conf;
		SyntAttributesXDF syntAttAnt = new SyntAttributesXDF();
		SyntAttributesXDF syntAtt = new SyntAttributesXDF();
		syntAtt.setNodeName(nodeName);
		syntAttAnt = (SyntAttributesXDF) syntVec.elementAt(k);
		
		for (Iterator<Configuration> it = this.iterator(); it.hasNext();) {
			conf = (Configuration) it.next();
			syntAtt = conf.validateData(syntAtt, data, posAt, nodeName,htIntersXfd);
			
			if(!syntAtt.isEmpty()) {
				  if (!syntAttAnt.isEmpty()) {  
				         syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				         syntVec.setElementAt(syntAttAnt,k);
				  }       
				  else 
					  syntVec.setElementAt(syntAtt,k);	  
			} else 
				syntVec.setElementAt(syntAttAnt,k);
		}
		
		//System.err.println("Return SyntVec: "+syntVec.toString());
		return syntVec;
	}
	
	/**
	 * This function make validation key in Node
	 * 
	 * @param syntVecAnt is Vector<SyntAttributes>
	 * @param k is integer
	 * @param syntAtt is SyntAttributes 
	 * @param posAnt is String
	 * @param posAt is String
	 * @param nodeName is String
	 * @param htContextXfd is HashTable
	 * @param htIntersXfd is HashTable<String,ArrayList<Inters>> 
	 * 
	 * @return Vector SyntAttributes
	 */
	public Vector<SyntAttributesXDF> keyValidationInNode(Vector<SyntAttributesXDF> syntVecAnt, int k, SyntAttributesXDF syntAtt,
			                          				  String posAnt, String posAt, String nodeName, Hashtable<String, Hashtable<ArrayList<String>, String>> htContextXfd, 
			                          				  Hashtable<String,ArrayList<Inters>> htIntersXfd, Hashtable<String, ArrayList<String>> hterror) {
		Configuration conf;
		
		SyntAttributesXDF syntAttAnt = (SyntAttributesXDF) syntVecAnt.elementAt(k);  //Attributes from parent node, for the same xdf 
		
		for (Iterator<Configuration> it = this.iterator(); it.hasNext();) {
			SyntAttributesXDF syntAttAux = new SyntAttributesXDF();
			conf = (Configuration) it.next();
			//System.out.println("Configuration: "+conf.toString());
			
			syntAttAux = conf.validateInNode(syntAtt, syntAttAnt, posAnt, posAt, nodeName, htContextXfd, htIntersXfd, hterror);
			
			if(!syntAttAux.isEmpty())
				syntVecAnt.setElementAt(syntAttAux, k);			
		}
		
		return syntVecAnt;
	}

	public int Role(Configuration c) {
		Fsa f = c.getFsa();
		State sf = c.getState();
		StateSet setF = f.get_final_states();
		AbstractConstraint k = ((AbstractConstraint) c.getKey());
		
		this.setKconf(k);
		
		int fposition = k.getFsaPosition(f);
		
		if (fposition == 3){ //T"
			if (setF.contains(sf))
				return 1; 
			else
				return 3;
		}
		
		if (fposition == 2){ //T'
			if (setF.contains(sf))
				return 2;
			else
				return 4;
		}
		
		if (fposition == 1){ //M
			if (setF.contains(sf))
				return 5;
			else
				return 6;
		}
		
		return 0;
	}
		
	public String toString() {
		String str = new String();
		
		for (Iterator<Configuration> i = this.iterator(); i.hasNext();) {
			Configuration c = (Configuration) i.next();
			str += c.toString();
			str += ",";
		}
		
		return str;
	}

	public void setKconf(AbstractConstraint kconf) {
		this.kconf = kconf;
	}

	public AbstractConstraint getKconf() {
		return kconf;
	}

	public void setRef(String ref) {
		this.ref = ref;
	}

	public String getRef() {
		return ref;
	}
	
	/* funcoes que nao estao sendo usadas */
	/*
	public SyntAttributes keyValidation(SyntAttributes sa, Vector v, XDF kc, int posk,	String p, String tagname) {
		SyntAttributes saNew = new SyntAttributes();
		int roleConf;

		if (!this.isEmpty()) {
			for (Iterator<Configuration> i = this.iterator(); i.hasNext();) {
				
				Configuration c = (Configuration) i.next();
				roleConf = Role(c);
				
			//	saNew.addAll(keyValidationRole(sa, v, kc, roleConf, posk, p, tagname));
			}
		}
		
		return saNew;
	}*/
	
	/*
	public SyntAttributes keyValidationRole(SyntAttributes sa, Vector v, XDF kc, int role, int posk,
											String p, String tagname) {	
		SyntAttributes syntAtt = new SyntAttributes();
		int refCount;

		if (role == 1) {
			// the node is the context node of a key
			// remove all the lists from the list and check
			// Synt Attributes Part
			ArrayList al3 = new ArrayList();
			ArrayList al2 = new ArrayList();

			for (Iterator n = sa.getTargetList().iterator(); n.hasNext();) {
				try {
					al3 = (ArrayList) n.next();
					al2.add(al3);	
				}
				catch (Exception e) {}
			}

			ArrayList al1 = (ArrayList) al2.clone();
			for (Iterator j = al2.iterator(); j.hasNext();) {
				al3 = (ArrayList) j.next();
				if (!al1.isEmpty()) {
					al1.remove(al3);
					if (al1.contains(al3)) {
						syntAtt.addContextList("f");
						String kn = kc.getNameConstraint();
						//Initial.ErrorMessage += "Duplicated value "
						//		+ al3.toString() + " for key " + kn + " \n";
						//System.out.println("ERRO :"
						//		+ Initial.ErrorMessage);
					}
				}
			}
			
			if (!syntAtt.containsContextList("f"))
				syntAtt.addContextList("t");
			else {
				syntAtt.clearContextList();
				syntAtt.addContextList("f");
			}
			
			return syntAtt;
		}

		if (role == 2) {
			// the node is the target node of the key
			// Synt Attributes Part
			ArrayList<String> al2 = new ArrayList<String>();
			al2.add("NULL");
			if (sa.containsKeyList("NULL")) {
				syntAtt.addTargetList(al2);
			} else {
				int n = kconf.getNPaths();
				
				if (sa.sizeKeyList() != n)
					syntAtt.addTargetList(al2);
				else
					syntAtt.addTargetList(sa.getKeyList());
			}
			
			return syntAtt;
		}

		if (role == 3) {
			// the node is part of a key
			// SyntAtt Part
			for (Iterator<String> j = sa.getKeyList().iterator(); j.hasNext();)
				syntAtt.addKeyList((String) j.next());
			
			return syntAtt;
		}

		if (role == 4) {
			ArrayList al3 = new ArrayList();
			ArrayList al2 = new ArrayList();

			SyntAttributes syntAtt1 = new SyntAttributes();
			ArrayList<AbstractConstraint> al4 = (ArrayList<AbstractConstraint>) ke.getKeysSet();

			for (Iterator n = sa.getTargetList().iterator(); n.hasNext();) {
				try {
					al3 = (ArrayList) n.next();
					al2.add(al3);
				} catch (Exception e) {
					System.out.println("Error at context for a foreign key :"+ e);
				}
			}

			for (int j = 0; j < v.size(); j++) {
				XDF k = (XDF) al4.get(j);
				String kn = k.getNameConstraint();
				
				if (kn.equals(ref)) {
					syntAtt1 = (SyntAttributes) v.elementAt(j);
				}
			}

			 //System.out.println("XDF List: "+syntAtt1.toString());

			for (Iterator n = syntAtt1.getTargetList().iterator(); n.hasNext();) {
				try {
					al3 = (ArrayList) n.next();
					refCount = 0;
					
					while (al2.contains(al3)){
						refCount++;
						al2.remove(al3);
					}
				} catch (Exception e){
					//System.out.println("Error in FK validation");
				}
			}

			// Here we verify if the foreign key is respected in this context.
			if (al2.isEmpty())
				syntAtt.addContextList("t");
			else
				syntAtt.addContextList("f");
		}
		
		// For Paths:
		if (role == 5)// path for key components
			syntAtt.addAllKey(sa);

		if (role == 6)// path for target
			syntAtt.addAllTarget(sa);
		
		if (role == 8)// path for context
			syntAtt.addAllContext(sa);
		
		return syntAtt;
	}*/
}
